/*
 * 础光实时操作系统PhotonRTOS -- AUTOSAR闹钟实现
 *
 * Copyright (C) 2022, 2023 国科础石(重庆)软件有限公司
 *
 * 作者: Baoyou Xie <xiebaoyou@kernelsoft.com>
 *
 * License terms: GNU General Public License (GPL) version 3
 *
 */

#include <autosar/internal.h>

/**
 * 所有有效警报，在StartOS中根据osek_alarm_attrs生成
 */
VAR(struct osek_alarm, AUTOMATIC) osek_alarms[OS_NR_ALARM] = {
};

FUNC(void, OS_CODE) __osek_alarm_callback(
	P2VAR(struct autosar_counter_waiter,AUTOMATIC,OS_APPL_DATA) waiter)
{
	if (waiter == NULL) {
		return;
	}

	if (waiter->cycle == 0) {
		((struct osek_alarm *)(waiter->param))->activated = 0;
	}
#if defined(AUTOSAR_ALARM_CALLBACK)
	if ((waiter->param != NULL)
		&& (((struct osek_alarm *)(waiter->param))->callback != NULL)) {
		CONTEXT_MARK_START(ENV_ALARM_CALLBACK)
		((struct osek_alarm *)(waiter->param))->callback();
		CONTEXT_MARK_END
	}
#endif
}

/**
 * 返回以滴答表示的、警报<AlarmID>超时值。
 * 语法：
 *     StatusType GetAlarm( AlarmType <AlarmID>, TickRefType <Tick>)
 * 参数（输入）：
 *     AlarmID			对警报的引用
 *  参数（输出）：
 *     Tick		以滴答表示的，
 *             在警报<AlarmID>超时相对值(相对于当前时间)
 * 特殊说明：
 *     由应用程序（例如 CancelAlarm）决定是否仍然有用。
 *     如果<AlarmID>不在使用中，<Tick>未定义。
 *     允许在任务级、ISR以及几个钩子例程中调用
 *     （参见图12.1）
 * 状态：
 *     标准：
 *        无错误，E_OK
 *        警报<AlarmID>未在使用中，E_OS_NOFUNC
 *    扩展：
 *        警报<AlarmID>无效，E_OS_ID
 * 一致性：
 *     BCC1, BCC2, ECC1, ECC2
 */
FUNC(StatusType, OS_CODE) GetAlarm(
	VAR(AlarmType, AUTOMATIC) AlarmID,
	VAR(TickRefType, AUTOMATIC) Tick)
{
	VAR(StatusType, AUTOMATIC) ret = E_OK;
	VAR(TickType, AUTOMATIC) now = 0;

	CONTEXT_CHECK(ENV_TASK | ENV_CAT2_ISR | ENV_ERROR_HOOK \
		| ENV_PRETASK_HOOK | ENV_POSTTASK_HOOK)

#if defined(EXTENDED_STATUS)
	if (AlarmID >=OS_NR_ALARM) {
		/**
		 * 根据OSEK规范，当ID过大时，返回E_OS_ID
		 * 注意这里AlarmID为无符号数
		 */
		ret = E_OS_ID;
		goto out;
	}
#endif

	if (osek_alarms[AlarmID].activated == 0) { /* 未激活的警报 */
		ret = E_OS_NOFUNC;
		goto out;
	}

	SuspendAllInterrupts();

	GetCounterValue(osek_alarms[AlarmID].counterID, &now);
	if (osek_alarms[AlarmID].waiter.time > now) {
		*Tick = osek_alarms[AlarmID].waiter.time - now;
	} else {
		*Tick = 0;
	}

	ResumeAllInterrupts();


out:
	/**
	 * 如果出现错误，就调用错误钩子
	 * 如果没有配置错误钩子宏，
	 * 编译器会做优化，不用担心。
	 */
	if (ret != E_OK) {
		osek_call_hook_1(GetAlarm, ret, AlarmID);
	}

	return ret;
}

/**
 * 该系统服务终止警报<AlarmID>。
 * 语法：
 *     StatusType CancelAlarm ( AlarmType <AlarmID> )
 * 参数（输入）：
 *     AlarmID		 对警报的引用
 * 参数（输出）：
 *     无
 * 特殊说明：
 *     允许在任务级、ISR中使用，
 *     但是不允许在钩子例程中使用。
 * 状态：
 *     标准：
 *         无错误，E_OK
 *         警报<AlarmID>不在使用中，E_OS_NOFUNC
 *     扩展：
 *         警报<AlarmID>无效，E_OS_ID
 * 一致性：
 *     BCC1, BCC2, ECC1, ECC2
 */
FUNC(StatusType, OS_CODE) CancelAlarm(
	VAR(AlarmType, AUTOMATIC) AlarmID)
{
	VAR(StatusType, AUTOMATIC) ret = E_OK;

	CONTEXT_CHECK(ENV_TASK | ENV_CAT2_ISR)

#if defined(EXTENDED_STATUS)
	/**
	 * 在扩展模式下，检查传入参数是否过大
	 */
	if (AlarmID >=OS_NR_ALARM) {
		/**
		 * 根据OSEK规范，当ID过大时，返回E_OS_ID
		 * 注意这里AlarmType为无符号数
		 */
		ret = E_OS_ID;
		goto out;
	}
#endif

	/**
	 * 这里关闭中断，其原因是避免与定时器中断产生竞争条件
	 */
	SuspendAllInterrupts();

	if (!osek_alarms[AlarmID].activated) { /* 未激活 */
		ret = E_OS_NOFUNC;
	} else { /* 已经激活，将其状态设置为未激活 */
		osek_alarms[AlarmID].activated = 0;
		remove_waiter_from_counter(&(osek_alarms[AlarmID].waiter),
			osek_alarms[AlarmID].counterID);
	}

	/**
	 * 恢复中断
	 */
	ResumeAllInterrupts();

out:
	/**
	 * 如果出现错误，就调用错误钩子
	 * 如果没有配置错误钩子宏，
	 * 编译器会做优化，不用担心。
	 */
	if (ret != E_OK) {
		osek_call_hook_1(CancelAlarm, ret, AlarmID);
	}

	return ret;
}

/**
 * 读取警报的base特征。
 * 返回值<Info>是一个数据结构，
 * 在该结构中存储AlarmBaseType类型的信息。
 * 语法：
 *     StatusType GetAlarmBase ( AlarmType <AlarmID>, AlarmBaseRefType <Info> )
 * 参数（输入）：
 *     AlarmID			对警报的引用
 * 参数（输出）：
 *     info				对警报base常量值的引用
 * 特殊说明：
 *     允许在任务级、ISR以及几个钩子例程中（参见图12.1）。
 * 状态：
 *     标准：
 *        无错误，E_OK
 *     扩展：
 *        警报< AlarmID>无效，E_OS_ID
 *  一致性：
 *     BCC1, BCC2, ECC1, ECC2
 */
FUNC(StatusType, OS_CODE) GetAlarmBase(
	VAR(AlarmType, AUTOMATIC) AlarmID,
	VAR(AlarmBaseRefType, AUTOMATIC) Info)
{
	VAR(StatusType, AUTOMATIC) ret = E_OK;

	CONTEXT_CHECK(ENV_TASK | ENV_CAT2_ISR | ENV_ERROR_HOOK \
		| ENV_PRETASK_HOOK | ENV_POSTTASK_HOOK)

#if defined(EXTENDED_STATUS)
	if (AlarmID >=OS_NR_ALARM) {
		/**
		 * 根据OSEK规范，当ID过大时，返回E_OS_ID
		 * 注意这里AlarmID为无符号数
		 */
		ret = E_OS_ID;
		goto out;
	}
#endif

	Info->maxallowedvalue = autosar_counters[osek_alarms[AlarmID].counterID].max_value;
	Info->ticksperbase = autosar_counters[osek_alarms[AlarmID].counterID].ticks_per_base;
	Info->mincycle = autosar_counters[osek_alarms[AlarmID].counterID].min_cycle;

out:
	/**
	 * 如果出现错误，就调用错误钩子
	 * 如果没有配置错误钩子宏，
	 * 编译器会做优化，不用担心。
	 */
	if (ret != E_OK) {
		osek_call_hook_2(GetAlarmBase, ret, AlarmID, Info);
	}

	return ret;
}

/**
 * 该系统服务占据警报<AlarmID>
 * 在<start>滴答到达后，与警报<AlarmID>相关的任务被激活
 * 或者相关事件（仅扩展任务）被设置
 * 或者警报回调函数被调用。
 * 语法：
 *     StatusType SetAbsAlarm ( AlarmType <AlarmID>,
 *             TickType <start>,
 *             TickType <cycle> )
 * 参数（输入）：
 *     AlarmID		对警报的引用
 *     start		以滴答表示的绝对值
 *     cycle        在循环警报的情况下，表示循环次数。
 *                  在单次警报情况下，其值应当为0
 * 参数（输出）：
 *     无
 * 特殊说明：
 *     如果绝对值<start>非常接近于当前计数值，
 *     警报可能会在系统服务返回用户时超时
 *     并且任务可能变为就绪状态，
 *     或者警报回调可能被调用。
 *     如果在调用系统调用前，绝对值<start>已经到达，
 *     那么警报仅仅应当在绝对值再次到达的时候到期。
 *     也就是说，在下一次溢出计数器的之后。
 *     如果 <cycle> 不等于零，
 *     则到期后立即使用相对值 <cycle>再次记录警报。
 *     警报<AlarmID>不应当在使用中。
 *     要修改已经在使用中的警报值，应当首先中止它。
 *     如果警报已经在用，该调用将被忽略，
 *     并且返回错误E_OS_STSTE。
 *     允许在任务级、ISR中使用，
 *     但是不允许在钩子例程中使用。
 * 状态：
 *     标准：
 *        无错误，E_OK
 *        警报<AlarmID>已经在使用中，E_OS_STATE
 *     扩展：
 *        警报<AlarmID>无效，E_OS_ID
 *        <start> 的值超出允许限制
 *       （小于零或大于 maxallowedvalue），E_OS_VALUE
 *        <cycle> 的值不等于 0 且超出允许的计数器限制
 *       （小于 mincycle 或大于 maxallowedvalue），E_OS_VALUE
 * 一致性：
 *     BCC1, BCC2, ECC1, ECC2
 */
FUNC(StatusType, OS_CODE) SetAbsAlarm(
	VAR(AlarmType, AUTOMATIC) AlarmID,
	VAR(TickType, AUTOMATIC) Start,
	VAR(TickType, AUTOMATIC) Cycle)
{
	VAR(StatusType, AUTOMATIC) ret = E_OK;

	CONTEXT_CHECK(ENV_TASK | ENV_CAT2_ISR)

#if defined(EXTENDED_STATUS)
	/**
	 * 在扩展模式下，检查传入参数是否过大
	 */
	if (AlarmID >=OS_NR_ALARM) {
		/**
		 * 根据OSEK规范，当ID过大时，返回E_OS_ID
		 * 注意这里AlarmType为无符号数
		 */
		ret = E_OS_ID;
		goto out;
	}

	if (Start > autosar_counters[osek_alarms[AlarmID].counterID].max_value) {
		ret = E_OS_VALUE;
		goto out;
	}

	if (Cycle > autosar_counters[osek_alarms[AlarmID].counterID].max_value) {
		ret = E_OS_VALUE;
		goto out;
	}

	if ((Cycle != 0) && (Cycle < autosar_counters[osek_alarms[AlarmID].counterID].min_cycle)) {
		ret = E_OS_VALUE;
		goto out;
	}
#endif

	/**
	 * 警报已经在激活状态，不能设置其值
	 */
	if (osek_alarms[AlarmID].activated != 0) {
		ret = E_OS_STATE;
		goto out;
	}

	SuspendAllInterrupts();

	osek_alarms[AlarmID].activated = 1;

	osek_alarms[AlarmID].waiter.time = Start;
	osek_alarms[AlarmID].waiter.cycle = Cycle;
	add_waiter_to_counter(&(osek_alarms[AlarmID].waiter), osek_alarms[AlarmID].counterID);

	/**
	 * 注意，这里可能有重调度
	 */
	ResumeAllInterrupts();

out:
	/**
	 * 如果出现错误，就调用错误钩子
	 * 如果没有配置错误钩子宏，
	 * 编译器会做优化，不用担心。
	 */
	if (ret != E_OK) {
		osek_call_hook_3(SetAbsAlarm, ret, AlarmID, Start, Cycle);
	}

	return ret;
}

/**
 * 占用警报<AlarmID>
 * 在<increment>次滴答过去后，与警报<AlarmID>相关的任务被激活
 * 或者相关事件（仅扩展任务）被设置，
 * 或者警报回调函数被调用。
 * 语法：
 *     StatusType SetRelAlarm ( AlarmType <AlarmID>,
 *          TickType <increment>,
 *          TickType <cycle> )
 * 参数（输入）：
 *     AlarmID		对警报的引用
 *     increment	以滴答表示的相对值
 *     cycle		在循环警报的情况下，表示循环次数。
 *                  在单次警报情况下，其值应当为0
 * 参数（输出）：
 *     无
 * 特殊说明：
 *     <increment> 等于 0 的行为取决于实现。
 *     如果相对值 <increment> 非常小
 *     那么在系统服务返回之前，警报可能会过期，
 *     并且任务可能变为就绪状态，
 *     或者警报回调函数可能被调用。
 *     该系统服务占用警报<AlarmID>，
 *     在<increment>次滴答过去后，
 *     与警报<AlarmID>相关的任务被激活或者
 *     相关事件（仅扩展任务）被设置，
 *     或者警报回调函数被调用。
 *     如果 <cycle> 不等于零，
 *     则警报在到期后立即以相对值 <cycle> 再次运行。
 *     警报<AlarmID>必须不在使用中。
 *     要改变已经在用的警报值，应当首先中止它。
 *     如果警报已经在使用中，该调用将被忽略，
 *     并且返回E_OS_STATE。
 *     允许在任务级、ISR中调用，但是不允许在钩子例程中。
 * 状态：
 *     标准：
 *        无错误，E_OK
 *        警报<AlarmID>已经在使用中，E_OS_STATE
 *     扩展：
 *        警报<AlarmID>无效，E_OS_ID
 *        <increment> 的值超出允许限制
 *        （小于零或大于 maxallowedvalue），E_OS_VALUE
 *        <cycle> 的值不等于 0 且超出允许的计数器限制
 *        （小于 mincycle 或大于 maxallowedvalue），E_OS_VALUE
 * 一致性：
 *     BCC1, BCC2, ECC1, ECC2
 */
FUNC(StatusType, OS_CODE) SetRelAlarm(
	VAR(AlarmType, AUTOMATIC) AlarmID,
	VAR(TickType, AUTOMATIC) Increment,
	VAR(TickType, AUTOMATIC) Cycle)
{
	VAR(StatusType, AUTOMATIC) ret = E_OK;
	VAR(TickType, AUTOMATIC) value = 0;
	VAR(TickType, AUTOMATIC) max_value = autosar_counters[osek_alarms[AlarmID].counterID].max_value;

	CONTEXT_CHECK(ENV_TASK | ENV_CAT2_ISR)
	
#if defined(EXTENDED_STATUS)
	/**
	 * 在扩展模式下，检查传入参数是否过大
	 */
	if (AlarmID >=OS_NR_ALARM) {
		/**
		 * 根据OSEK规范，当ID过大时，返回E_OS_ID
		 * 注意这里AlarmType为无符号数
		 */
		ret = E_OS_ID;
		goto out;
	}

	if (Increment > max_value) {
		ret = E_OS_VALUE;
		goto out;
	}

	if (Cycle > max_value) {
		ret = E_OS_VALUE;
		goto out;
	}

	if ((Cycle != 0) && (Cycle < autosar_counters[osek_alarms[AlarmID].counterID].min_cycle)) {
		ret = E_OS_VALUE;
		goto out;
	}
#endif

	/**
	 * 警报已经在激活状态，不能设置其值
	 */
	if (osek_alarms[AlarmID].activated != 0) {
		ret = E_OS_STATE;
		goto out;
	}

	SuspendAllInterrupts();

	osek_alarms[AlarmID].activated = 1;

	GetCounterValue(osek_alarms[AlarmID].counterID, &value);
	value = Increment + value;
	if (value > max_value) {
		value = value - (max_value + 1);
	}
	osek_alarms[AlarmID].waiter.time = value;
	osek_alarms[AlarmID].waiter.cycle = Cycle;
	add_waiter_to_counter(&(osek_alarms[AlarmID].waiter), osek_alarms[AlarmID].counterID);

	/**
	 * 注意，这里可能有重调度
	 */
	ResumeAllInterrupts();

out:
	/**
	 * 如果出现错误，就调用错误钩子
	 * 如果没有配置错误钩子宏，
	 * 编译器会做优化，不用担心。
	 */
	if (ret != E_OK) {
		osek_call_hook_3(SetRelAlarm, ret, AlarmID, Increment, Cycle);
	}

	return ret;
}
